/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		acleditor rows.cpp
 * PURPOSE:		acl dialog row management
 *				included as part of acleditor.cpp
 */

class AclEditorRows : public AclEditorColumns
{
	USHORT RowSize;

protected:
	AclEditorRows(AclEditorInitParams const &Init) :
		RowSize(0x20),
		AclEditorColumns(Init)
	{
		Rows.realloc_ct(RowSize);
	}

private:
	void ExpandRowsIf()
	{
		if(Rows.get_ct() < static_cast<size_t>(RowMax) + 1)
			Rows.increase_ct(RowMax + 1);
	}

	void RemoveDataRow(USHORT Index)
	{
		if(Index == MAXUSHORT)
			return;

		Rows[Index]->dec_ref();

		RtlMoveMemory(Rows + Index, Rows + Index + 1, (RowMax - Index) * sizeof(ListboxAceRow *));

		RowMax--;

		ResetScroll();
	}

	void SwapDataRows(ULONG Index1, ULONG Index2)
	{
		if(Index1 == MAXUSHORT || Index2 == MAXUSHORT)
			return;

		ListboxAceRow *Temp = Rows[Index1];

		Rows[Index1] = Rows[Index2];

		Rows[Index2] = Temp;
	}

	void InsertDataRow(ListboxAceRow *Row, int Index)
	{
		RowMax++;

		ExpandRowsIf();

		RtlMoveMemory(Rows + Index + 1, Rows + Index, (RowMax - Index) * sizeof(ListboxAceRow *));

		Row->inc_ref();
		Rows[Index] = Row;

		ResetScroll();
	}

	void ClearDataRows()
	{
		if(RowMax != MAXUSHORT)
			for(USHORT i = 0; i <= RowMax; i++)
				Rows[i]->dec_ref();

		RowMax = MAXUSHORT;
	}

protected:
	void AddDataRow(ListboxAceRow *Row)
	{
		RowMax++;

		ExpandRowsIf();

		Row->inc_ref();

		Rows[RowMax] = Row;

		ResetScroll();
	}

	USHORT GetDataRowIndex(ListboxAceRow *Row)
	{
		if(RowMax != MAXUSHORT)
			for(USHORT i = 0; i <= RowMax; i++)
				if(Rows[i] == Row)
					return i;

		RaiseException(STATUS_ROW_DELETED, EXCEPTION_NONCONTINUABLE, 0, NULL);

		return 0; //not actually reachable, here to placate the compiler
	}

	void InsertNewRow(BOOL after)
	{
		OpText Ot(L"Inserting a new row");

		int pos = (int)SendMessage(Columns[COL_TSTE_SID].Wnd, LB_GETCURSEL, NULL, NULL);

		if(after || pos == -1)
			pos++;

		InsertDataRow(new ListboxAceRow(this, Columns, 0), pos);

		for(int i = COL_MIN_ACE; i <= COL_MAX_ACE; i++)
			if(Columns[i].Wnd)
				if(i == COL_ACE_TYPE)
				{
					if(PreviousAclState & ACL_FLAG_LABEL_ACL)
						RaiseSendMessageListBox(Columns[i].Wnd, LB_INSERTSTRING, pos, reinterpret_cast<LPARAM>(AceTypeLabels[SYSTEM_MANDATORY_LABEL_ACE_TYPE]));
					else
						RaiseSendMessageListBox(Columns[i].Wnd, LB_INSERTSTRING, pos, reinterpret_cast<LPARAM>(AceTypeLabels[ACCESS_ALLOWED_ACE_TYPE]));
				}
				else
					RaiseSendMessageListBox(Columns[i].Wnd, LB_INSERTSTRING, pos, reinterpret_cast<LPARAM>(L""));

		SyncAceSelection(pos);
		Ot;
	}

	void SyncAceSelection(int newrow)
	{
		for(UCHAR i = COL_MIN_ACE; i <= COL_MAX_ACE_SEL; i++)
			RaiseSendMessageListBox(Columns[i].Wnd, LB_SETCURSEL, newrow, NULL);

		ScrollForVisible(newrow);
	}

	void SwapRows(int src, int dest)
	{
		SwapDataRows(src, dest == -1 ? RowMax : dest);

		for(int i = COL_MIN_ACE; i <= COL_MAX_ACE; i++)
			if(Columns[i].Wnd)
			{
				LRESULT selected;
				LRESULT insertpos;

				selected = RaiseSendMessageListBox(Columns[i].Wnd, LB_GETSEL, src, NULL);

				auto_szpod<WCHAR> text = GetNewListBoxText(Columns[i].Wnd, src);

				RaiseSendMessageListBox(Columns[i].Wnd, LB_DELETESTRING, src, NULL);

				insertpos = RaiseSendMessageListBox(Columns[i].Wnd, LB_INSERTSTRING, dest, (LPARAM)text.get());

				if(selected && (GetWindowLong(Columns[i].Wnd, GWL_STYLE) & LBS_MULTIPLESEL))
					RaiseSendMessageListBox(Columns[i].Wnd, LB_SETSEL, TRUE, insertpos);
			}
	}

	void SwapRows(BOOL up)
	{
		OpText Ot(L"Shifting a row");

		if(RowMax == MAXUSHORT)
			return;

		int pos = (int)RaiseSendMessageListBox(Columns[COL_MIN_ACE].Wnd, LB_GETCURSEL, NULL, NULL);
		int size = (int)RaiseSendMessageListBox(Columns[COL_MIN_ACE].Wnd, LB_GETCOUNT, NULL, NULL) - 1;
		int dest;

		if(up)
			dest = pos - 1;
		else
			dest = pos + 1;

		if(dest == size)
			dest = -1;
		
		if(dest > size)
			dest = 0;

		SwapRows(pos, dest);
		SyncAceSelection(dest == -1 ? size : dest);
		Ot;
	}

	void DeleteRow()
	{
		OpText Ot(L"Deleting a row");

		int pos = GetSelectedRowIndex(COL_MIN_ACE);

		if(pos == -1)
			return;

		for(int i = COL_MIN_ACE; i <= COL_MAX_ACE; i++)
			if(Columns[i].Wnd)
				SendMessage(Columns[i].Wnd, LB_DELETESTRING, pos, NULL);

		RemoveDataRow(narrowing_cast<USHORT>(pos));

		if(RowMax != MAXUSHORT)
			SyncAceSelection(min(RowMax, pos));

		ResetScroll();
		Ot;
	}

	void ClearRows()
	{
		OpText Ot(L"Clearing rows");

		for(int i = COL_MIN_ACE; i <= COL_MAX_ACE; i++)
			if(Columns[i].Wnd)
				SendMessage(Columns[i].Wnd, LB_RESETCONTENT, NULL, NULL);

		ClearDataRows();

		ResetScroll();
		Ot;
	}

	USHORT GetSelectedRowIndex(UCHAR ColumnIndex = COL_TSTE_NAME)
	{
		return (USHORT)SendMessage(Columns[ColumnIndex].Wnd, LB_GETCURSEL, NULL, NULL);
	}

	~AclEditorRows()
	{
		ClearDataRows();
	}
};